package puzzle.hashikake.data;

import puzzle.common.data.HatanException;

public class HashiState {
	public static final int NUMBER_UNDECIDED = -1;

	public final HashiDef def;

	private HashikakeBoard m_board;
	private int m_number;
	private int m_possibilityFlag;
	private boolean m_isAssumed;

	public HashiState(HashikakeBoard b, HashiDef def) {
		this.def = def;
		m_board = b;
		m_number = NUMBER_UNDECIDED;
		m_possibilityFlag = 0x07;
		m_isAssumed = false;
	}

	public HashiState(HashikakeBoard b, HashiState other) {
		this.def = other.def;
		m_board = b;
		m_number = other.m_number;
		m_possibilityFlag = other.m_possibilityFlag;
		m_isAssumed = other.m_isAssumed;
	}

	public HashiState dup(HashikakeBoard b) {
		return new HashiState(b, this);
	}

	public boolean isFixed() {
		return m_number >= 0;
	}

	public boolean isPossible(int number) {
		return (m_possibilityFlag & (1 << number)) != 0;
	}

	public int getPossibilityFlag() {
		return m_possibilityFlag;
	}

	public int getNumber() {
		return m_number;
	}

	public boolean setNumber(int number) throws HatanException {
		boolean isChanged = false;
		if (m_number != number) {
			if (isFixed()) {
				throw new HatanException();
			}

			m_number = number;
			m_possibilityFlag = (1 << number); // sync state
			isChanged |= true;

			if (number > 0) {
				isChanged |= forbidCrossings();
			}
		}
		return isChanged;
	}

	private boolean forbidCrossings() throws HatanException {
		boolean isChanged = false;
		for (HashiDef hashi : def.getCrossings()) {
			HashiState hashiState = m_board.getHashiState(hashi.i);
			isChanged |= hashiState.setNumber(0);
		}
		return isChanged;
	}

	public boolean setPossibilityFlag(int flag) throws HatanException {
		boolean isChanged = false;
		if (m_possibilityFlag != flag) {
			m_possibilityFlag = flag;
			switch (flag) {
			case 0:
				throw new HatanException();
			case 1:
				isChanged |= setNumber(0);
				break;
			case 2:
				isChanged |= setNumber(1);
				break;
			case 4:
				isChanged |= setNumber(2);
				break;
			case 6:
				isChanged |= forbidCrossings();
			}
		}
		return isChanged;
	}

	public boolean setForbidden(int n) throws HatanException {
		return setPossibilityFlag(m_possibilityFlag & ~(1 << n));
	}

	public void setAssumed() {
		m_isAssumed = true;
	}

	public boolean isAssumed() {
		return m_isAssumed;
	}

}
